/*
    服务端
    简单版的断开重连
    IO 多路复用 select
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/select.h>

int main()
{
    char *port = "8888";
    char *ip = "0.0.0.0";
    // 一样先创建socket对象
    int sever_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(sever_fd == -1)
    {
        perror("创建socket对象失败！");
        exit(1);
    }
    // 如果某个端口处于time_wait状态，我们可以通过如下的这个setsockopt函数设置socket重新使用time_wait端口
    int reuse = 1;
    setsockopt(sever_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    // 设置端口，开始监听
    struct sockaddr_in addr;
    addr.sin_addr.s_addr = inet_addr(ip);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(port));
    // 参数 绑定的那个socket对象，绑定的端口的端口 结构体大小
    int ret = bind(sever_fd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret == -1)
        perror("bind");
    // 开始监听 ,并设置队列的大小
    listen(sever_fd, 10);

    // 用来保存下一次循环监听谁
    fd_set base_set;    
    FD_ZERO(&base_set);
    FD_SET(sever_fd, &base_set);

    int new_fd;
    while(1)
    {
        fd_set set;
        FD_ZERO(&set);
        // 将下一次要监听的文件描述符集合 复制给set
        memcpy(&set, &base_set, sizeof(base_set));
        
        select(11, &set, NULL, NULL, NULL);
        if(FD_ISSET(sever_fd, &set))
        {
            // 此时此刻，有新连接来了
            // 来了新链接
            new_fd = accept(sever_fd, NULL, NULL);
            // 转向监听标准输入与通信的socket对象
            FD_ZERO(&base_set);
            FD_SET(new_fd, &base_set);
            FD_SET(STDIN_FILENO, &base_set);
            
            // 下一次循环监听对象变化了
            continue;
        }

        // 如果stdin在集合中就说明 stdin准备就绪
        if(FD_ISSET(STDIN_FILENO, &set))
        {
            // 标准输入可读
            char buf[60];
            memset(buf, 0, sizeof(buf));
            read(STDIN_FILENO, buf, sizeof(buf));
            send(new_fd, buf, sizeof(buf), 0);
        }
        if(FD_ISSET(new_fd, &set))
        {
            // 和对方通信的socket对象就绪，有数据发过来了
            char buf[60];
            memset(buf, 0, sizeof(buf));
            int size = recv(new_fd, buf, sizeof(buf), 0);
            // select 认为new_fd可读，不阻塞，但是没有读到数据，所以对方断开连接了
            if(size == 0)
            {
                // 对方断开连接，转向监听用来三次握手的socket对象
                FD_ZERO(&base_set);
                FD_SET(sever_fd, &base_set);

                // 对方已经关闭
                close(new_fd);
                continue;
            }
            printf("buf:%s\n", buf);
        }
    }
    close(sever_fd);
    return 0;
}